package com.kmxd.ams.app.use;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.CharSequenceUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.kmxd.ams.app.search.SearchEsSvc;
import com.kmxd.ams.client.archive.IArchiveTypeSvc;
import com.kmxd.ams.client.archive.vo.ArchiveTypeListVO;
import com.kmxd.ams.client.arrange.IBoxSvc;
import com.kmxd.ams.client.arrange.IDocumentSvc;
import com.kmxd.ams.client.arrange.IProjectSvc;
import com.kmxd.ams.client.arrange.IVolumeSvc;
import com.kmxd.ams.client.search.dto.SearchAuthQueryDTO;
import com.kmxd.ams.client.search.vo.SearchAuthVO;
import com.kmxd.ams.client.system.ISysFondsSvc;
import com.kmxd.ams.client.system.vo.SysFondsListVO;
import com.kmxd.ams.client.use.IUseBorrowCarSvc;
import com.kmxd.ams.client.use.dto.*;
import com.kmxd.ams.client.use.vo.UseBorrowCarListVO;
import com.kmxd.ams.client.use.vo.UseBorrowCarVO;
import com.kmxd.ams.core.common.constant.ErrorCode;
import com.kmxd.ams.core.common.enums.ArchStatusEnum;
import com.kmxd.ams.core.common.query.QueryGen;
import com.kmxd.ams.core.exception.BizTipException;
import com.kmxd.ams.core.util.ConvertUtil;
import com.kmxd.ams.core.util.IdUtil;
import com.kmxd.ams.core.util.SecurityUtil;
import com.kmxd.ams.infra.archive.entity.ArchiveType;
import com.kmxd.ams.infra.arrange.base.SystemBO;
import com.kmxd.ams.infra.arrange.entity.Box;
import com.kmxd.ams.infra.arrange.entity.Document;
import com.kmxd.ams.infra.arrange.entity.Project;
import com.kmxd.ams.infra.arrange.entity.Volume;
import com.kmxd.ams.infra.system.entity.SysFonds;
import com.kmxd.ams.infra.tmpl.dto.ArchTmplBO;
import com.kmxd.ams.infra.use.entity.UseBorrowCar;
import com.kmxd.ams.infra.use.mapper.UseBorrowCarMapper;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import lombok.AllArgsConstructor;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.jetbrains.annotations.NotNull;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

/**
 * 借阅车表 服务实现类
 *
 * @author nickbi
 * @since 2023-01-10
 */
@Service
@AllArgsConstructor
public class UseBorrowCarSvcImpl extends ServiceImpl<UseBorrowCarMapper, UseBorrowCar>
    implements IUseBorrowCarSvc {

  private final IDocumentSvc documentSvc;
  private final IVolumeSvc volumeSvc;
  private final IArchiveTypeSvc archiveTypeSvc;
  private final ISysFondsSvc fondsSvc;
  private UseBorrowCarMapper useBorrowCarMapper;

  @Override
  public Page<UseBorrowCarListVO> selectPage(Page reqPage, UseBorrowCarQueryDTO req) {
    removeNotExistData(req);
    LambdaQueryWrapper<UseBorrowCar> queryWrapper =
        QueryGen.init(new QueryWrapper<UseBorrowCar>(), req).lambda();
    if (!SecurityUtil.isRootGroup()) {
      queryWrapper.eq(UseBorrowCar::getUserId, SecurityUtil.getUserId());
    }
    Page<UseBorrowCar> page = useBorrowCarMapper.selectPage(reqPage, queryWrapper);

    Page<UseBorrowCarListVO> voPage = BeanUtil.copyProperties(page, Page.class);
    if (ObjectUtil.isNull(page) || ObjectUtil.isEmpty(page.getRecords())) {
      return voPage;
    }
    voPage.setRecords(convertList(page.getRecords()));
    return voPage;
  }

  /**
   * 删除不存在的数据
   *
   * @param req
   */
  @Transactional(rollbackFor = Exception.class)
  public void removeNotExistData(UseBorrowCarQueryDTO req) {
    LambdaQueryWrapper<UseBorrowCar> queryWrapper =
        QueryGen.init(new QueryWrapper<UseBorrowCar>(), req).lambda();
    queryWrapper.eq(UseBorrowCar::getUserId, SecurityUtil.getUserId());
    List<UseBorrowCar> useBorrowCars = useBorrowCarMapper.selectList(queryWrapper);
    if (ObjectUtil.isEmpty(useBorrowCars)) {
      return;
    }
    Map<ImmutablePair<Long, String>, List<UseBorrowCar>> group =
        useBorrowCars.stream()
            .collect(
                Collectors.groupingBy(
                    item -> ImmutablePair.of(item.getArchTypeId(), item.getTableId())));
    List<Long> removeIds = Lists.newArrayList();
    group.forEach(
        (pair, list) -> {
          Long archTypeId = pair.getLeft();
          String tableId = pair.getRight();

          List<Long> ids = list.stream().map(UseBorrowCar::getArchId).collect(Collectors.toList());
          if (CharSequenceUtil.contains(tableId, ArchTmplBO.DOCUMENT)) {
            Document qryDoc = new Document();
            qryDoc.setDocIds(StrUtil.join(",", ids));
            qryDoc.setArchTypeId(archTypeId);
            qryDoc.setTableId(tableId);
            qryDoc.setStatus(ArchStatusEnum.S1.getCode());
            List<Document> documents = documentSvc.selectListWithNoDataScope(qryDoc);
            if (ObjectUtil.isEmpty(documents)) {
              return;
            }
            List<Long> docIds =
                documents.stream().map(SystemBO::getId).collect(Collectors.toList());
            List<Long> notExists =
                list.stream()
                    .filter(item -> !docIds.contains(item.getArchId()))
                    .map(UseBorrowCar::getId)
                    .collect(Collectors.toList());
            if (ObjectUtil.isEmpty(notExists)) {
              return;
            }
            removeIds.addAll(notExists);
          }

          if (CharSequenceUtil.contains(tableId, ArchTmplBO.VOLUME)) {
            Volume qryDoc = new Volume();
            qryDoc.setVolIds(StrUtil.join(",", ids));
            qryDoc.setArchTypeId(archTypeId);
            qryDoc.setTableId(tableId);
            qryDoc.setStatus(ArchStatusEnum.S1.getCode());
            List<Volume> documents = volumeSvc.selectListWithNoDataScope(qryDoc);
            if (ObjectUtil.isEmpty(documents)) {
              return;
            }
            List<Long> docIds =
                documents.stream().map(SystemBO::getId).collect(Collectors.toList());
            List<Long> notExists =
                list.stream()
                    .filter(item -> !docIds.contains(item.getArchId()))
                    .map(UseBorrowCar::getId)
                    .collect(Collectors.toList());
            if (ObjectUtil.isEmpty(notExists)) {
              return;
            }
            removeIds.addAll(notExists);
          }

          if (CharSequenceUtil.contains(tableId, ArchTmplBO.BOX)) {
            Box qryDoc = new Box();
            qryDoc.setBoxIds(StrUtil.join(",", ids));
            qryDoc.setArchTypeId(archTypeId);
            qryDoc.setTableId(tableId);
            qryDoc.setStatus(ArchStatusEnum.S1.getCode());
            IBoxSvc boxSvc = SpringUtil.getBean(IBoxSvc.class);
            List<Box> documents = boxSvc.selectListWithNoDataScope(qryDoc);
            if (ObjectUtil.isEmpty(documents)) {
              return;
            }
            List<Long> docIds =
                documents.stream().map(SystemBO::getId).collect(Collectors.toList());
            List<Long> notExists =
                list.stream()
                    .filter(item -> !docIds.contains(item.getArchId()))
                    .map(UseBorrowCar::getId)
                    .collect(Collectors.toList());
            if (ObjectUtil.isEmpty(notExists)) {
              return;
            }
            removeIds.addAll(notExists);
          }

          if (CharSequenceUtil.contains(tableId, ArchTmplBO.PROJECT)) {
            Project qryDoc = new Project();
            qryDoc.setProjIds(StrUtil.join(",", ids));
            qryDoc.setArchTypeId(archTypeId);
            qryDoc.setTableId(tableId);
            IProjectSvc projectSvc = SpringUtil.getBean(IProjectSvc.class);
            List<Project> documents = projectSvc.selectList(qryDoc);
            if (ObjectUtil.isEmpty(documents)) {
              return;
            }
            List<Long> docIds =
                documents.stream().map(SystemBO::getId).collect(Collectors.toList());
            List<Long> notExists =
                list.stream()
                    .filter(item -> !docIds.contains(item.getArchId()))
                    .map(UseBorrowCar::getId)
                    .collect(Collectors.toList());
            if (ObjectUtil.isEmpty(notExists)) {
              return;
            }
            removeIds.addAll(notExists);
          }
        });
    if (ObjectUtil.isEmpty(removeIds)) {
      return;
    }
    useBorrowCarMapper.deleteBatchIds(removeIds);
  }

  @Override
  public List<UseBorrowCarListVO> selectList(UseBorrowCarQueryDTO req) {
    removeNotExistData(req);
    LambdaQueryWrapper<UseBorrowCar> queryWrapper =
        QueryGen.init(new QueryWrapper<UseBorrowCar>(), req).lambda();
    if (!SecurityUtil.isRootGroup()) {
      queryWrapper.eq(UseBorrowCar::getUserId, SecurityUtil.getUserId());
    }
    List<UseBorrowCar> list = useBorrowCarMapper.selectList(queryWrapper);
    if (ObjectUtil.isEmpty(list)) {
      return Lists.newArrayList();
    }
    List<UseBorrowCarListVO> res = convertList(list);
    return res;
  }

  @Override
  public List<UseBorrowCarListVO> selectListByIds(
      Long archTypeId, String tableId, List<Long> archIds) {
    LambdaQueryWrapper<UseBorrowCar> queryWrapper = Wrappers.<UseBorrowCar>lambdaQuery();
    queryWrapper
        .eq(UseBorrowCar::getArchTypeId, archTypeId)
        .eq(UseBorrowCar::getTableId, tableId)
        .in(UseBorrowCar::getArchId, archIds);
    List<UseBorrowCar> list = useBorrowCarMapper.selectList(queryWrapper);
    return BeanUtil.copyToList(list, UseBorrowCarListVO.class);
  }

  @NotNull
  private List<UseBorrowCarListVO> convertList(List<UseBorrowCar> list) {
    if (ObjectUtil.isEmpty(list)) {
      return Lists.newArrayList();
    }
    List<ArchiveTypeListVO> archTypeList = getArchTypeList(new UseBorrowCarQueryDTO());
    Map<Long, String> archTypeNameMap =
        archTypeList.stream()
            .collect(Collectors.toMap(ArchiveTypeListVO::getId, ArchiveTypeListVO::getName));
    return list.stream()
        .map(
            car -> {
              UseBorrowCarListVO vo = BeanUtil.copyProperties(car, UseBorrowCarListVO.class);
              SearchEsSvc esSvc = SpringUtil.getBean(SearchEsSvc.class);
              SearchAuthQueryDTO qryAuth = new SearchAuthQueryDTO();
              qryAuth.setId(car.getArchId());
              qryAuth.setArchTypeId(car.getArchTypeId());
              qryAuth.setTableId(car.getTableId());
              SearchAuthVO auth = esSvc.getAuth(qryAuth);
              vo.setAuth(auth);
              vo.setArchTypeName(archTypeNameMap.get(vo.getArchTypeId()));
              if (CharSequenceUtil.contains(vo.getTableId(), ArchTmplBO.DOCUMENT)) {
                Document qryDoc = new Document();
                qryDoc.setArchTypeId(vo.getArchTypeId());
                qryDoc.setTableId(vo.getTableId());
                qryDoc.setId(vo.getArchId());
                Document view = documentSvc.view(qryDoc);
                vo.setFilesNum(view.getFilesNum());
                vo.setFillingNum(view.getFillingNum());
                vo.setStorageNum(view.getStorageNum());
              }
              return vo;
            })
        .collect(Collectors.toList());
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void add(UseBorrowCarAddDTO req) {
    UseBorrowCar entity = buildUseBorrowCar(req);
    useBorrowCarMapper.insert(entity);
  }

  @NotNull
  private UseBorrowCar buildUseBorrowCar(UseBorrowCarAddDTO req) {
    long count =
        count(
            Wrappers.<UseBorrowCar>lambdaQuery()
                .eq(UseBorrowCar::getArchTypeId, req.getArchTypeId())
                .eq(UseBorrowCar::getTableId, req.getTableId())
                .eq(UseBorrowCar::getArchId, req.getArchId()));
    if (count > 0) {
      throw BizTipException.instance(ErrorCode.VALIDATE_DATA_REPEAT, "请勿重复添加借阅车");
    }
    UseBorrowCar entity = BeanUtil.copyProperties(req, UseBorrowCar.class);
    entity.setComId(SecurityUtil.getCropId());
    entity.setFondsId(SecurityUtil.getFondsId());
    entity.setDeptId(SecurityUtil.getLoginUser().getDeptId());
    entity.setUserId(SecurityUtil.getUserId());
    if (req.getTableId().contains(ArchTmplBO.DOCUMENT)) {

      Document qryDoc = new Document();
      qryDoc.setArchTypeId(req.getArchTypeId());
      qryDoc.setTableId(req.getTableId());
      qryDoc.setId(req.getArchId());
      Document view =
          Optional.ofNullable(documentSvc.view(qryDoc))
              .orElseThrow(
                  () -> BizTipException.instance(ErrorCode.USE_ARCH_DATA_NOT_FOUND, "未找到档案"));
      entity.setArchCode(view.getArchCode());
      entity.setArchTitle(view.getTitle());
      entity.setDocDate(DateUtil.format(view.getDocDate(), DatePattern.NORM_DATE_PATTERN));
      entity.setDocNumber(view.getDocNumber());
      entity.setSecurity(view.getSecurity());
      entity.setYear(view.getYear());
      entity.setFillingNum(view.getFillingNum());
      entity.setFilesNum(view.getFilesNum());
      entity.setEType(view.getFileType());
    }
    if (req.getTableId().contains(ArchTmplBO.VOLUME)) {
      Volume qryDoc = new Volume();
      qryDoc.setArchTypeId(req.getArchTypeId());
      qryDoc.setTableId(req.getTableId());
      qryDoc.setId(req.getArchId());
      Volume view =
          Optional.ofNullable(volumeSvc.view(qryDoc))
              .orElseThrow(
                  () -> BizTipException.instance(ErrorCode.USE_ARCH_DATA_NOT_FOUND, "未找到档案"));
      entity.setArchCode(view.getArchCode());
      entity.setArchTitle(view.getTitle());
      entity.setDocDate(DateUtil.format(view.getVolumeDate(), DatePattern.NORM_DATE_PATTERN));
      entity.setSecurity(view.getSecurity());
      entity.setYear(view.getYear());
      entity.setFillingNum(view.getFillingNum());
      entity.setFilesNum(view.getFilesNum());
      entity.setEType(view.getFileType());
    }
    return entity;
  }

  @Override
  public void add(UseBorrowCarBatchAddDTO req) {

    if (ObjectUtil.isEmpty(req.getList())) {
      return;
    }
    List<UseBorrowCar> list =
        req.getList().stream().map(this::buildUseBorrowCar).collect(Collectors.toList());
    saveBatch(list);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void edit(UseBorrowCarEditDTO req) {
    UseBorrowCar entity = BeanUtil.copyProperties(req, UseBorrowCar.class);
    useBorrowCarMapper.updateById(entity);
  }

  @Override
  @Transactional(rollbackFor = Exception.class)
  public void remove(String ids) {
    Set<Long> idList = IdUtil.split(ids);
    this.baseMapper.deleteBatchIds(idList);
  }

  @Override
  public UseBorrowCarVO view(Long id) {
    UseBorrowCar entity = useBorrowCarMapper.selectById(id);
    return BeanUtil.copyProperties(entity, UseBorrowCarVO.class);
  }

  @Override
  public UseBorrowCarVO getBorrowCar(UseBorrowCarQueryDTO req) {
    UseBorrowCar useBorrowCar =
        useBorrowCarMapper.selectOne(
            Wrappers.<UseBorrowCar>lambdaQuery()
                .eq(UseBorrowCar::getArchId, req.getArchId())
                .eq(UseBorrowCar::getTableId, req.getTableId())
                .eq(UseBorrowCar::getArchTypeId, req.getArchTypeId())
                .last("limit 1"));
    return BeanUtil.copyProperties(useBorrowCar, UseBorrowCarVO.class);
  }

  @Override
  public void removeByArchId(UseBorrowCarRemoveDTO req) {
    useBorrowCarMapper.delete(
        Wrappers.<UseBorrowCar>lambdaQuery()
            .eq(UseBorrowCar::getArchId, req.getArchId())
            .eq(UseBorrowCar::getArchTypeId, req.getArchTypeId())
            .eq(UseBorrowCar::getTableId, req.getTableId()));
  }

  @Override
  public List<ArchiveTypeListVO> getArchTypeList(UseBorrowCarQueryDTO req) {
    QueryWrapper<UseBorrowCar> query = new QueryWrapper<>();
    query.select("DISTINCT arch_type_id");
    LambdaQueryWrapper<UseBorrowCar> lambda = query.lambda();
    lambda
        .eq(UseBorrowCar::getUserId, SecurityUtil.getUserId())
        .eq(ObjectUtil.isNotNull(req.getFondsId()), UseBorrowCar::getFondsId, req.getFondsId());
    List<UseBorrowCar> borrowCars = useBorrowCarMapper.selectList(lambda);
    if (ObjectUtil.isEmpty(borrowCars)) {
      return Lists.newArrayList();
    }
    List<Long> archTypeIds =
        borrowCars.stream().map(UseBorrowCar::getArchTypeId).collect(Collectors.toList());
    List<ArchiveType> archiveTypes = archiveTypeSvc.listByIds(archTypeIds);
    return ConvertUtil.toList(archiveTypes, ArchiveTypeListVO.class);
  }

  @Override
  public List<SysFondsListVO> getFondsList() {
    QueryWrapper<UseBorrowCar> query = new QueryWrapper<>();
    query.select("DISTINCT fonds_id");
    LambdaQueryWrapper<UseBorrowCar> lambda = query.lambda();
    lambda.eq(UseBorrowCar::getUserId, SecurityUtil.getUserId());
    List<UseBorrowCar> borrowCars = useBorrowCarMapper.selectList(lambda);
    if (ObjectUtil.isEmpty(borrowCars)) {
      return Lists.newArrayList();
    }
    List<Long> fondsIds =
        borrowCars.stream().map(UseBorrowCar::getFondsId).collect(Collectors.toList());
    List<SysFonds> sysFonds = fondsSvc.listByIds(fondsIds);
    return ConvertUtil.toList(sysFonds, SysFondsListVO.class);
  }
}
